package com.lzj.framework.config;

import com.lzj.framework.security.JwtAuthenticationTokenFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;

/**
 * @author 贱贱
 * @Description 安全配置
 * @Date 2024/07/29 14:24
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableWebSecurity
@Configuration
public class SecurityConfig {

    /**
     * 静态资源
     */
    private static final String[] STATIC_RESOURCE = {
        "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js",
        "/swagger-resources/**", "/swagger-ui.html", "/webjars/**", "/*/api-docs", "/*/api-docs/**",
        "/doc.html"
    };

    /**
     * 接口白名单
     */
    private static final String[] WHITE_URL = {
         "/" ,"/login", "/captcha/*", "/error", "/actuator/**", "/oss/**"
    };
    
    @Resource
    private CorsFilter corsFilter;

    /**
     * 安全配置链
     */
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http,
                                    AuthorizationManager<RequestAuthorizationContext> access,
                                    AuthenticationEntryPoint unauthorizedHandler,
                                    LogoutSuccessHandler logoutHandler) throws Exception {
        
        return http
            // 基于 token，不需要 csrf
            .csrf().disable()
            // 禁用HttpSession
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            //禁用Http响应头缓存数据
            .headers().cacheControl().disable().and()
            // 可以通过<frame>标签引用系统资源
            .headers().frameOptions().disable().and()
            // 过滤请求 - 请求权限配置
            .authorizeHttpRequests( authorize -> authorize
                // 白名单
                .antMatchers(WHITE_URL).permitAll()
                .antMatchers(STATIC_RESOURCE).permitAll()
                // 其他所有地址都会经过认证-授权过滤器
                .anyRequest().access(access)
            )
            // 认证失败处理器
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            // 认证过滤器
            .addFilterBefore(new JwtAuthenticationTokenFilter(), LogoutFilter.class)
            // 跨域过滤器
            .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
            // 注销处理
            .logout(logout -> logout.logoutSuccessHandler(logoutHandler))
            .build();
    }

    /**
     * 认证管理器
     */
    @Bean
    public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(daoAuthenticationProvider);
    }

    /**
     * 慢哈希算法加密
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
